package com.qfedu.labsystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qfedu.labsystem.config.JwtConfig;
import com.qfedu.labsystem.dao.UserMapper;
import com.qfedu.labsystem.entity.bo.UserBO;
import com.qfedu.labsystem.entity.dto.*;
import com.qfedu.labsystem.entity.enums.ResultEnum;
import com.qfedu.labsystem.entity.vo.UserVO;
import com.qfedu.labsystem.exception.BusinessRuntimeException;
import com.qfedu.labsystem.service.UserService;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.mail.internet.MimeMessage;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName UserServiceImpl
 * @Author ykx
 * @Date 2024/4/6 8:33
 * @Version 1.0
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserBO> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private JavaMailSender javaMailSender;

    @Autowired
    private Cipher cipher;

    @Autowired
    private RSAPrivateKey rsaPrivateKey;

    @Autowired
    private RSAPublicKey rsaPublicKey;

    @Override
    public PageInfo<UserVO> getUserList(BasePageDTO basePageDTO) {
        PageHelper.startPage(basePageDTO.getPage(), basePageDTO.getSize());
        List<UserVO> userList = userMapper.getUserList();
        return new PageInfo<>(userList);
    }

    @Override
    public Boolean addUser(UserBO userBO) {
        // 检查用户名是否存在
        QueryWrapper<UserBO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", userBO.getUsername());
        List<UserBO> list = list(queryWrapper);
        if (list != null && list.size() > 0) {
            throw new BusinessRuntimeException(ResultEnum.USERNAME_EXIST);
        }
        // 私钥加密
        userBO.setKeyPassword(encrypt(userBO.getPassword()));
        return save(userBO);
    }

    /**
     * 私钥加密
     *
     * @param password 密码
     * @return String
     */
    @SneakyThrows
    private String encrypt(String password) {
        cipher.init(Cipher.ENCRYPT_MODE, rsaPrivateKey);
        byte[] bytes = cipher.doFinal(password.getBytes());
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 公钥解密码
     *
     * @param keyPassword 密码
     * @return String
     */
    @SneakyThrows
    private String decrypt(String keyPassword) {
        cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
        byte[] decodeContent = cipher.doFinal(keyPassword.getBytes());
        return new String(decodeContent);
    }


    @Override

    public Boolean updateUser(UserBO userBO) {
        return updateById(userBO);
    }

    @Override
    public Boolean deleteUser(Integer id) {
        return removeById(id);
    }

    @Override
    public UserVO getUser(UserBO userBO) {
        // userBO.setKeyPassword(encrypt(userBO.getPassword()));
        UserVO currentUser = userMapper.getUser(userBO);
        if (redisTemplate.opsForValue().get(userBO.getUsername() + "_lock") != null) {
            throw new BusinessRuntimeException(ResultEnum.USER_LOCK);
        }

        if (currentUser != null) {
            // 账号密码正确,清除错误次数
            redisTemplate.delete(userBO.getUsername());

            currentUser.setToken(jwtConfig.createToken(currentUser.getUsername()));
        } else {
            // 账号密码错误，记录错误次数，错误次数达到5次，锁定账号
            Long increment = redisTemplate.opsForValue().increment(userBO.getUsername(), 1L);
            if (Objects.nonNull(increment) && increment >= 5) {
                redisTemplate.opsForValue().set(userBO.getUsername() + "_lock", "lock", 300, TimeUnit.SECONDS);
            }

            throw new BusinessRuntimeException(ResultEnum.USER_PASSWORD_ERROR);
        }

        return currentUser;
    }

    @Override
    public Boolean updatePwd(UpdatePwdDTO updatePwdDTO) {
        // 先获取到用户信息
        UserBO userBO = getById(updatePwdDTO.getId());
        if (!Objects.equals(userBO.getPassword(), updatePwdDTO.getPassword())) {
            throw new BusinessRuntimeException(ResultEnum.PASSWORD_ERROR);
        } else {
            userBO.setPassword(updatePwdDTO.getNewPassword());
        }
        return updateById(userBO);
    }

    @Override
    public Boolean updateInfo(UpdateInfoDTO updateInfoDTO) {
        UserBO userBO = getById(updateInfoDTO.getId());
        userBO.setTel(updateInfoDTO.getTel());
        userBO.setEmail(updateInfoDTO.getEmail());

        return updateById(userBO);
    }

    @Override
    @SneakyThrows
    public Boolean forgetPwd(ForgetPwdDTO forgetPwdDTO) {
        // 先查询有没有这个用户
        QueryWrapper<UserBO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", forgetPwdDTO.getUsername());

        UserBO userBO = getOne(queryWrapper);
        if (Objects.isNull(userBO)) {
            throw new BusinessRuntimeException(ResultEnum.USERNAME_NOT_EXIST);
        } else {
            // 发送链接到邮箱
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom("498517614@qq.com");
            helper.setTo(forgetPwdDTO.getEmail());
            helper.setSubject("重置密码");
            helper.setText("点击此处重置密码链接:http://localhost:5173/#/forget,仅5分钟有效，请尽快修改", Boolean.TRUE);
            javaMailSender.send(message);
        }

        // 设置当前key:{username}+resetPwd至缓存数据库，过期时间为5分钟
        redisTemplate.opsForValue().set(forgetPwdDTO.getUsername() + "_resetPwd", "resetPwd", 300, TimeUnit.SECONDS);

        return Boolean.TRUE;
    }

    @Override
    public Boolean resetPwd(ResetPwdDTO resetPwdDTO) {
        String resetPwd = redisTemplate.opsForValue().get(resetPwdDTO.getUsername() + "_resetPwd");
        if (StringUtils.isEmpty(resetPwd)) {
            throw new BusinessRuntimeException(ResultEnum.NO_AUTH_RESET);
        } else {
            // 查找用户
            QueryWrapper<UserBO> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("username", resetPwdDTO.getUsername());

            // 修改密码
            UserBO userBO = getOne(queryWrapper);
            userBO.setPassword(resetPwdDTO.getNewPassword());
            updateById(userBO);

            redisTemplate.delete(resetPwdDTO.getUsername() + "_resetPwd");
        }
        return Boolean.TRUE;
    }

}
